from dataclasses import dataclass
from typing import List, Tuple
import pyqtgraph as pg
from PySide6.QtCore import Qt

@dataclass
class Point:
    """点数据结构"""
    x: float
    y: float
    z: float = 0
    
    @classmethod
    def from_string(cls, value: str):
        """从字符串创建点"""
        x, y, z = map(float, value.split())
        return cls(x, y, z)

class BaseFeature:
    """加工特征基类"""
    def __init__(self):
        self.is_selected = False
        self.plot_items = []  # 存储绘图项目
        self.default_pen = None  # 初始画笔
        
    def draw(self, plot_widget: pg.PlotWidget):
        """绘制特征"""
        raise NotImplementedError
        
    def contains_point(self, x: float, y: float) -> bool:
        """判断点是否在特征内"""
        raise NotImplementedError
        
    def highlight(self, enabled: bool):
        """高亮显示"""
        for item in self.plot_items:
            if enabled:
                item.setPen(pg.mkPen('y', width=3))
            else:
                item.setPen(self.default_pen)

class HoleFeature(BaseFeature):
    """孔特征"""
    def __init__(self, center: Point, diameter: float):
        super().__init__()
        self.center = center
        self.diameter = diameter
        self.default_pen = pg.mkPen('g', width=2)
        
    def draw(self, plot_widget: pg.PlotWidget):
        """
        绘制圆孔
        数学原理：使用圆的参数方程
        x = x0 + r * cos(t)
        y = y0 + r * sin(t)
        """
        import numpy as np
        t = np.linspace(0, 2*np.pi, 100)
        x = self.center.x + self.diameter/2 * np.cos(t)
        y = self.center.y + self.diameter/2 * np.sin(t)
        
        circle = plot_widget.plot(x, y, pen=self.default_pen)
        self.plot_items.append(circle)
        
    def contains_point(self, x: float, y: float) -> bool:
        """
        判断点是否在圆内
        数学原理：点到圆心距离 <= 半径
        """
        dx = x - self.center.x
        dy = y - self.center.y
        return (dx*dx + dy*dy) <= (self.diameter/2)**2

class SlotFeature(BaseFeature):
    """槽特征"""
    def __init__(self, start: Point, end: Point, width: float):
        super().__init__()
        self.start = start
        self.end = end
        self.width = width
        self.default_pen = pg.mkPen('r', width=2)
        
    def draw(self, plot_widget: pg.PlotWidget):
        """
        绘制槽线
        数学原理：
        1. 中心线连接起点终点
        2. 平行线偏移 width/2 距离
        """
        # 绘制中心线
        center_line = plot_widget.plot(
            [self.start.x, self.end.x],
            [self.start.y, self.end.y],
            pen=self.default_pen
        )
        self.plot_items.append(center_line)
        
        # 计算法向量
        dx = self.end.x - self.start.x
        dy = self.end.y - self.start.y
        length = (dx*dx + dy*dy)**0.5
        if length > 0:
            nx = -dy/length * self.width/2
            ny = dx/length * self.width/2
            
            # 绘制边界线
            edge1 = plot_widget.plot(
                [self.start.x + nx, self.end.x + nx],
                [self.start.y + ny, self.end.y + ny],
                pen=pg.mkPen('r', width=1, style=Qt.DashLine)
            )
            edge2 = plot_widget.plot(
                [self.start.x - nx, self.end.x - nx],
                [self.start.y - ny, self.end.y - ny],
                pen=pg.mkPen('r', width=1, style=Qt.DashLine)
            )
            self.plot_items.extend([edge1, edge2])
            
    def contains_point(self, x: float, y: float) -> bool:
        """
        判断点是否在槽线范围内
        数学原理：
        1. 点到直线距离 <= width/2
        2. 点在起点和终点之间
        """
        # 计算点到直线距离
        dx = self.end.x - self.start.x
        dy = self.end.y - self.start.y
        length = (dx*dx + dy*dy)**0.5
        if length == 0:
            return False
            
        # 点到直线距离公式：|(x-x1)(y2-y1)-(y-y1)(x2-x1)|/sqrt((x2-x1)^2+(y2-y1)^2)
        distance = abs((x-self.start.x)*dy - (y-self.start.y)*dx) / length
        
        # 判断点是否在起点终点之间

        dot_product = (x-self.start.x)*dx + (y-self.start.y)*dy
        return distance <= self.width/2 and 0 <= dot_product <= length*length